<template>
  <div ref="outerWrapper" :class="wrapperClasses">
    <div v-if="isHorizontal" :class="`${prefix}-horizontal`">
      <div :style="{right: `${anotherOffset}%`}" :class="[`${prefix}-pane`, 'left-pane']">
        <slot name="left"/>
      </div>
      <div :class="`${prefix}-trigger-con`" :style="{left: `${offset}%`}" @mousedown="handleMousedown">
        <slot name="trigger">
          <trigger mode="vertical"/>
        </slot>
      </div>
      <div :style="{left: `${offset}%`}" :class="[`${prefix}-pane`, 'right-pane']">
        <slot name="right"/>
      </div>
    </div>
    <div v-else :class="`${prefix}-vertical`">
      <div :style="{bottom: `${anotherOffset}%`}" :class="[`${prefix}-pane`, 'top-pane']">
        <slot name="top"/>
      </div>
      <div :class="`${prefix}-trigger-con`" :style="{top: `${offset}%`}" @mousedown="handleMousedown">
        <slot name="trigger">
          <trigger mode="horizontal"/>
        </slot>
      </div>
      <div :style="{top: `${offset}%`}" :class="[`${prefix}-pane`, 'bottom-pane']">
        <slot name="bottom"/>
      </div>
    </div>
  </div>
</template>

<script>
  import { off, on, oneOf } from '@/libs/tools'
  import Trigger from './trigger.vue'

  export default {
    name: 'SplitPane',
    components: {
      Trigger
    },
    props: {
      value: {
        type: [Number, String],
        default: 0.5
      },
      mode: {
        validator (value) {
          return oneOf(value, ['horizontal', 'vertical'])
        },
        default: 'horizontal'
      },
      min: {
        type: [Number, String],
        default: '40px'
      },
      max: {
        type: [Number, String],
        default: '40px'
      }
    },
    /**
     * Events
     * @on-move-start
     * @on-moving 返回值：事件对象，但是在事件对象中加入了两个参数：atMin(当前是否在最小值处), atMax(当前是否在最大值处)
     * @on-move-end
     */
    data () {
      return {
        prefix: 'ivu-split',
        offset: 0,
        oldOffset: 0,
        isMoving: false
      }
    },
    computed: {
      wrapperClasses () {
        return [
          `${this.prefix}-wrapper`,
          this.isMoving ? 'no-select' : ''
        ]
      },
      isHorizontal () {
        return this.mode === 'horizontal'
      },
      anotherOffset () {
        return 100 - this.offset
      },
      valueIsPx () {
        return typeof this.value === 'string'
      },
      offsetSize () {
        return this.isHorizontal ? 'offsetWidth' : 'offsetHeight'
      },
      computedMin () {
        return this.getComputedThresholdValue('min')
      },
      computedMax () {
        return this.getComputedThresholdValue('max')
      }
    },
    methods: {
      px2percent (numerator, denominator) {
        return parseFloat(numerator) / parseFloat(denominator)
      },
      getComputedThresholdValue (type) {
        let size = this.$refs.outerWrapper[this.offsetSize]
        if (this.valueIsPx) return typeof this[type] === 'string' ? this[type] : size * this[type]
        else return typeof this[type] === 'string' ? this.px2percent(this[type], size) : this[type]
      },
      getMin (value1, value2) {
        if (this.valueIsPx) return `${Math.min(parseFloat(value1), parseFloat(value2))}px`
        else return Math.min(value1, value2)
      },
      getMax (value1, value2) {
        if (this.valueIsPx) return `${Math.max(parseFloat(value1), parseFloat(value2))}px`
        else return Math.max(value1, value2)
      },
      getAnotherOffset (value) {
        let res = 0
        if (this.valueIsPx) res = `${this.$refs.outerWrapper[this.offsetSize] - parseFloat(value)}px`
        else res = 1 - value
        return res
      },
      handleMove (e) {
        let pageOffset = this.isHorizontal ? e.pageX : e.pageY
        let offset = pageOffset - this.initOffset
        let outerWidth = this.$refs.outerWrapper[this.offsetSize]
        let value = this.valueIsPx ? `${parseFloat(this.oldOffset) + offset}px` : (this.px2percent(outerWidth * this.oldOffset + offset, outerWidth))
        let anotherValue = this.getAnotherOffset(value)
        if (parseFloat(value) <= parseFloat(this.computedMin)) value = this.getMax(value, this.computedMin)
        if (parseFloat(anotherValue) <= parseFloat(this.computedMax)) value = this.getAnotherOffset(this.getMax(anotherValue, this.computedMax))
        e.atMin = this.value === this.computedMin
        e.atMax = this.valueIsPx ? this.getAnotherOffset(this.value) === this.computedMax : this.getAnotherOffset(this.value).toFixed(5) === this.computedMax.toFixed(5)
        this.$emit('input', value)
        this.$emit('on-moving', e)
      },
      handleUp () {
        this.isMoving = false
        off(document, 'mousemove', this.handleMove)
        off(document, 'mouseup', this.handleUp)
        this.$emit('on-move-end')
      },
      handleMousedown (e) {
        this.initOffset = this.isHorizontal ? e.pageX : e.pageY
        this.oldOffset = this.value
        this.isMoving = true
        on(document, 'mousemove', this.handleMove)
        on(document, 'mouseup', this.handleUp)
        this.$emit('on-move-start')
      }
    },
    watch: {
      value () {
        this.offset = (this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000 / 100
      }
    },
    mounted () {
      this.$nextTick(() => {
        this.offset = (this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000 / 100
      })
    }
  }
</script>

<style lang="less">
  @import './index.less';
</style>
